package es.uca.plii.warrenl0;

import es.uca.plii.helpers.OutputChannel;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * La clase Main es la clase principal, punto de entrada del programa.
 * @author Rafael Sánchez, Alfonso Sendra
 */
public class Main {

    /**
     * Instancia del programa ejecutable
     */
    private static Main exe = new Main();

    /**
     * Constante que indica que el formato deseado es Flattened
     */
    private final static Integer FLATTENED = 0;

    /**
     * Constante que indica que el formato deseado es Tokenized
     */
    private final static Integer TOKENIZED = 1;

    /**
     * Constante que indica que el formato deseado es el código M0
     */
    private final static Integer INSTRUCTIONS = 2;


    /**
     * Crea una instancia de la clase Main, extrae los argumentos, y llama
     * al método de instancia ejecutar.
     * @param args
     */
    public static void main(String[] args) {

        String fileName = null;

        String outputFile = null;

        Integer formatoDeseado = Main.INSTRUCTIONS;

        if (args.length < 1) {
            exe.showUsage();
            return;
        }

        if (args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                String a = args[i];

                if (a.length() >= 3 && a.substring(0, 2).equals("--")) {

                    if (a.equals("--help")) {
                        exe.showUsage();
                        return;

                    } else if (a.equals("--tokenized")) {

                        formatoDeseado = Main.TOKENIZED;

                    } else if (a.equals("--flattened")) {

                        formatoDeseado = Main.FLATTENED;

                    } else if (a.equals("--output")) {
                        i++;
                        if (i == args.length) {
                            exe.showUsage();
                            return;
                        }
                        String fn = args[i];
                        if (fn.length() >= 3 && fn.substring(0, 2).equals("--")) {
                            exe.showUsage();
                            return;
                        }
                        outputFile = fn;
                    } else {
                        exe.showUsage();
                    }
                } else {
                    if (fileName != null) {
                        exe.showUsage();
                        return;
                    } else {
                        fileName = a;
                    }
                }
            }

        }

        exe.ejecutar(fileName, outputFile, formatoDeseado);

    }

    /**
     * Muestra le texto de ayuda
     */
    private void showUsage() {
        showBanner();
        System.out.println("Uso:  prolog0  --help | [--tokekized | --flattened] [--output ficheroSalida] ficheroEntrada \n\n");
        System.out.println("   ficheroEntrada");
        System.out.println("                 Archivo con el programa a compilar.");
        System.out.println("   --help        Muestra esta ayuda");
        System.out.println("   --output ficheroSalida");
        System.out.println("                 Dirige la salida al fichero especificado");
        System.out.println("   --tokenized   Devuelve el formato \"tokenized\" de query y program en lugar de");
        System.out.println("                  las instrucciones de salida de M0");
        System.out.println("   --flattened   Devuelve el formato \"flattened\" de query y program en lugar de");
        System.out.println("                  las instrucciones de salida de M0");
    }

    /**
     * Muestra el "banner" que se imprime en la consola siempre que se ejecuta
     * el programa, excepto cuando se solicita la salida del código M0.
     */
    private void showBanner() {
        System.out.println("Implementacion de L0 para la maquina abstracta de Warren");
        System.out.println("Autores: Rafael Sanchez y Alfonso Sendra\n");
    }

    /**
     * Ejecuta el programa, realizando el análisis sintáctico, y posteriormente
     * creando las entidades los objetos del tipo Programa y Query, a partir
     * en el resultado del análisis<br/>
     * Finalmente, según el tipo de formato deseado, se dirige al canal de
     * salida el resultado.
     * @param sourcename Nombre del archivo con el código L0
     * @param outputfile Nombre del archivo se salida
     * @param formatoDeseado Formato deseado por el usuario
     */
    private void ejecutar(String sourcename, String outputfile, Integer formatoDeseado) {
        try {
            if(!formatoDeseado.equals(Main.INSTRUCTIONS))
                showBanner();
            WarrenExpLexer lex = new WarrenExpLexer(new FileReader(sourcename));
            WarrenExpParser parser = new WarrenExpParser(lex);
            OutputChannel.inicializa(outputfile);
            parser.parse();
            Expresion exp = Expresion.getInstance();
            Nodo nodoPrograma = exp.getPrograma();
            Nodo nodoQuery = exp.getQuery();

            Query query = new Query(nodoQuery);
            Programa programa = new Programa(nodoPrograma);

            if (formatoDeseado.equals(Main.FLATTENED)) {

                OutputChannel.escribe("Query  Flattened Form:\n");
                OutputChannel.escribe(query.flattenedForm());
                OutputChannel.escribe("\n\n");
                OutputChannel.escribe("Program Flattened Form:\n");
                OutputChannel.escribe(programa.flattenedForm());
                OutputChannel.escribe("\n");

            } else if (formatoDeseado.equals(Main.TOKENIZED)) {

                OutputChannel.escribe("Query Tokenized Form: \n");
                OutputChannel.escribe(query.tokenizedForm());
                OutputChannel.escribe("\n");
                OutputChannel.escribe("Program Tokenized Form: \n");
                OutputChannel.escribe(programa.tokenizedForm());
                OutputChannel.escribe("\n");

            } else {

                OutputChannel.escribe(query.instructionForm());
                OutputChannel.escribe(programa.instructionForm());
            }

            OutputChannel.terminar();


        } catch (FileNotFoundException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        } catch (Exception ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }


    }
}
